/*
 *  MarchovicConcreteNoteGeneratorTester.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/10/10.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "MarchovicConcreteNoteGeneratorTester.h"
#include "../../../sc/Tester/CustomAssert.h"
#include "../System0039.h"

#include "../MarchovicConcreteNoteGenerator.h"

namespace sc
{
namespace system0039
{

	void MarchovicConcreteNoteGeneratorTester::TestAll()
	{
		Test_GetConcreteNote000();
		Test_GetConcreteNote001();
		Test_GetConcreteNote002();
		Test_GetConcreteNote003();
	}

	void MarchovicConcreteNoteGeneratorTester::Test_GetConcreteNote000()
	{
		double density = 1.0;
		double dynamics = Note::piano;
		::sc::hs::Sound::WaveType waveType = ::sc::hs::Sound::SingleSineWave;
		ConcreteNote note0( waveType, dynamics, Note::DSharp2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note1( waveType, dynamics, Note::E2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note2( waveType, dynamics, Note::F2, 1.0, Note::D1, 2.0 ); 
		double duration = ( note0.GetDuration() + note1.GetDuration() + note2.GetDuration() ) / 3.0;

		StochasticConcreteNote destinationNote;
		MarchovicConcreteNoteGenerator generator( density, duration );

		destinationNote.AddNote( note1, 1.0 );
		generator.AddChain( note0, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note2, 1.0 );
		generator.AddChain( note1, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note2, destinationNote );

//		cout << generator.ToString() << endl;
		ConcreteNote currentNote = generator.GetConcreteNote();
		int repeatCount = 10000;
		for ( int i = 0; i < repeatCount; i++ )
		{
			ConcreteNote note = generator.GetConcreteNote();
			if ( currentNote == note0 )
			{
			}
			else if ( currentNote == note1 )
			{
				AssertTrue( note == note2 );
			}
			else if ( currentNote == note2 )
			{
				AssertTrue( note == note0 );
			}
			else
			{
				AssertFail();
			}
			currentNote = note;
		}
	}

	void MarchovicConcreteNoteGeneratorTester::Test_GetConcreteNote001()
	{
		double density = 1.0;
		double dynamics = Note::piano;
		::sc::hs::Sound::WaveType waveType = ::sc::hs::Sound::SingleSineWave;
		ConcreteNote note0( waveType, dynamics, Note::DSharp2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note1( waveType, dynamics, Note::E2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note2( waveType, dynamics, Note::F2, 1.0, Note::D1, 2.0 ); 
		double duration = ( note0.GetDuration() + note1.GetDuration() + note2.GetDuration() ) / 3.0;

		StochasticConcreteNote destinationNote;
		MarchovicConcreteNoteGenerator generator( density, duration );

		destinationNote.AddNote( note1, 1.0 );
		destinationNote.AddNote( note2, 1.0 );
		generator.AddChain( note0, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note1, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note2, destinationNote );

		ConcreteNote currentNote = generator.GetConcreteNote();
		int repeatCount = 10000;
		int countOfDSharpToE = 0;
		int countOfDSharpToF = 0;
		int countOfEToDSharp = 0;
		int countOfFToDSharp = 0;
		for ( int i = 0; i < repeatCount; i++ )
		{
//			cout << generator.ToString() << endl;
			ConcreteNote note = generator.GetConcreteNote();
			if ( currentNote == note0 )
			{
				if ( note == note1 )
				{
					countOfDSharpToE++;
				}
				else if ( note ==note2 )
				{
					countOfDSharpToF++;
				}
				else
				{
					AssertFail();
				}
			}
			else if ( currentNote == note1 )
			{
				AssertTrue( note == note0 );
				countOfEToDSharp++;
			}
			else if ( currentNote == note2 )
			{
				AssertTrue( note == note0 );
				countOfFToDSharp++;
			}
			else
			{
				AssertFail();
			}
			currentNote = note;
		}

		AssertLessOrEqual( 0.90 * countOfDSharpToE, countOfDSharpToF ); 
		AssertLessOrEqual( countOfDSharpToF, 1.10 * countOfDSharpToE ); 
	}

	void MarchovicConcreteNoteGeneratorTester::Test_GetConcreteNote002()
	{
		double density = 0.7;
		double dynamics = Note::piano;
		::sc::hs::Sound::WaveType waveType = ::sc::hs::Sound::SingleSineWave;
		ConcreteNote note0( waveType, dynamics, Note::DSharp2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note1( waveType, dynamics, Note::E2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note2( waveType, dynamics, Note::F2, 1.0, Note::D1, 2.0 ); 
		double duration = ( note0.GetDuration() + note1.GetDuration() + note2.GetDuration() ) / 3.0;

		StochasticConcreteNote destinationNote;
		MarchovicConcreteNoteGenerator generator( density, duration );

		destinationNote.AddNote( note1, 1.0 );
		generator.AddChain( note0, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note2, 1.0 );
		generator.AddChain( note1, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note2, destinationNote );

		ConcreteNote currentNote;
		while ( true )
		{
			currentNote = generator.GetConcreteNote();
			if ( currentNote.GetDynamics() )
			{
				break;
			}
		}

		int repeatCount = 10000;
		int countOfMute = 0;
		int countOfNote0 = 0;
		int countOfNote1 = 0;
		int countOfNote2 = 0;
		for ( int i = 0; i < repeatCount; i++ )
		{
			ConcreteNote note = generator.GetConcreteNote();
			if ( note.GetDynamics() == 0 )
			{
				countOfMute++;
				continue;
			}
			if ( currentNote == note0 )
			{
				AssertTrue( note == note1 );
				countOfNote0++;
			}
			else if ( currentNote == note1 )
			{
				AssertTrue( note == note2 );
				countOfNote1++;
			}
			else if ( currentNote == note2 )
			{
				AssertTrue( note == note0 );
				countOfNote2++;
			}
			else
			{
				AssertFail();
			}
			currentNote = note;
		}
		
		AssertLessOrEqual( 0.9 * ( 1.0 - density ) * repeatCount, countOfMute );
		AssertLessOrEqual( countOfMute, 1.10 * ( 1.0 - density ) * repeatCount );
		AssertLessOrEqual( ( 1.0 / 3.0 ) * 0.9 * density * repeatCount, countOfNote0 );
		AssertLessOrEqual( countOfNote0, ( 1.0 / 3.0 ) * 1.10 * density * repeatCount );
		AssertLessOrEqual( ( 1.0 / 3.0 ) * 0.9 * density * repeatCount, countOfNote1 );
		AssertLessOrEqual( countOfNote1, ( 1.0 / 3.0 ) * 1.10 * density * repeatCount );
		AssertLessOrEqual( ( 1.0 / 3.0 ) * 0.9 * density * repeatCount, countOfNote2 );
		AssertLessOrEqual( countOfNote2, ( 1.0 / 3.0 ) * 1.10 * density * repeatCount );
	}

	void MarchovicConcreteNoteGeneratorTester::Test_GetConcreteNote003()
	{
		double density = 0.7;
		double dynamics = Note::piano;
		::sc::hs::Sound::WaveType waveType = ::sc::hs::Sound::SingleSineWave;
		ConcreteNote note0( waveType, dynamics, Note::DSharp2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note1( waveType, dynamics, Note::E2, 1.0, Note::D1, 2.0 ); 
		ConcreteNote note2( waveType, dynamics, Note::F2, 1.0, Note::D1, 2.0 ); 
		double duration = ( note0.GetDuration() + note1.GetDuration() + note2.GetDuration() ) / 3.0;

		StochasticConcreteNote destinationNote;
		MarchovicConcreteNoteGenerator generator( density, duration );

		destinationNote.AddNote( note1, 1.0 );
		destinationNote.AddNote( note2, 1.0 );
		generator.AddChain( note0, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note1, destinationNote );

		destinationNote.Clear();
		destinationNote.AddNote( note0, 1.0 );
		generator.AddChain( note2, destinationNote );

		ConcreteNote currentNote = generator.GetConcreteNote();
		int repeatCount = 10000;
		int countOfDSharpToE = 0;
		int countOfDSharpToF = 0;
		int countOfEToDSharp = 0;
		int countOfFToDSharp = 0;
		int countOfMute = 0;
		for ( int i = 0; i < repeatCount; i++ )
		{
			ConcreteNote note = generator.GetConcreteNote();
			if ( note.GetDynamics() == 0.0 )
			{
				countOfMute++;
				continue;
			}
			if ( currentNote == note0 )
			{
				if ( note == note1 )
				{
					countOfDSharpToE++;
				}
				else if ( note ==note2 )
				{
					countOfDSharpToF++;
				}
				else
				{
					AssertFail();
				}
			}
			else if ( currentNote == note1 )
			{
				AssertTrue( note == note0 );
				countOfEToDSharp++;
			}
			else if ( currentNote == note2 )
			{
				AssertTrue( note == note0 );
				countOfFToDSharp++;
			}
			currentNote = note;
		}

		int countOfNoteToNote = countOfDSharpToE + countOfDSharpToF + countOfEToDSharp + countOfFToDSharp;
		AssertLessOrEqual( 0.90 * density * repeatCount, countOfNoteToNote );
		AssertLessOrEqual( countOfNoteToNote, 1.10 * density * repeatCount );

		AssertLessOrEqual( 0.90 * ( 1 - density )* repeatCount, countOfMute );
		AssertLessOrEqual( countOfMute, 1.10 * ( 1 - density )* repeatCount );
	}

}
}
